﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using Common.Utilities;
using Common.Utilities.Dispatcher;

namespace Workflows.Components.TextMining.Collapse
{
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class EntityLinkInstruction:InstructionBase
    {
        #region src-1
        private string _ConnStr1;
        /// <summary></summary>
        public string ConnStr1
        {
            get
            {
                return this._ConnStr1;
            }
            set
            {
                this._ConnStr1 = value;
            }
        }

        private string _SchemaName1;
        /// <summary></summary>
        public string SchemaName1
        {
            get
            {
                return this._SchemaName1;
            }
            set
            {
                this._SchemaName1 = value;
            }
        }

        private string _TableName1;
        /// <summary></summary>
        public string TableName1
        {
            get
            {
                return this._TableName1;
            }
            set
            {
                this._TableName1 = value;
            }
        }

        private string _Sql1;
        /// <summary></summary>
        public string Sql1
        {
            get
            {
                return this._Sql1;
            }
            set
            {
                this._Sql1 = value;
            }
        }

        private Dictionary<string,string> _FieldTransformations1;
        /// <summary></summary>
        public Dictionary<string,string> FieldTransformations1
        {
            get
            {
                return this._FieldTransformations1;
            }
            set
            {
                this._FieldTransformations1 = value;
            }
        }

        private List<string> _DescFields1;
        /// <summary></summary>
        public List<string> DescFields1
        {
            get
            {
                return this._DescFields1;
            }
            set
            {
                this._DescFields1 = value;
            }
        }

        private List<string> _AssocFields1;
        /// <summary></summary>
        public List<string> AssocFields1
        {
            get
            {
                return this._AssocFields1;
            }
            set
            {
                this._AssocFields1 = value;
            }
        }

        private string _PKField1;
        /// <summary></summary>
        public string PKField1
        {
            get
            {
                return this._PKField1;
            }
            set
            {
                this._PKField1 = value;
            }
        }
        #endregion

        #region src-2
        private string _ConnStr2;
        /// <summary></summary>
        public string ConnStr2
        {
            get
            {
                return this._ConnStr2;
            }
            set
            {
                this._ConnStr2 = value;
            }
        }

        private string _SchemaName2;
        /// <summary></summary>
        public string SchemaName2
        {
            get
            {
                return this._SchemaName2;
            }
            set
            {
                this._SchemaName2 = value;
            }
        }

        private string _TableName2;
        /// <summary></summary>
        public string TableName2
        {
            get
            {
                return this._TableName2;
            }
            set
            {
                this._TableName2 = value;
            }
        }

        private string _Sql2;
        /// <summary></summary>
        public string Sql2
        {
            get
            {
                return this._Sql2;
            }
            set
            {
                this._Sql2 = value;
            }
        }

        private Dictionary<string, string> _FieldTransformations2;
        /// <summary></summary>
        public Dictionary<string, string> FieldTransformations2
        {
            get
            {
                return this._FieldTransformations2;
            }
            set
            {
                this._FieldTransformations2 = value;
            }
        }

        private List<string> _DescFields2;
        /// <summary></summary>
        public List<string> DescFields2
        {
            get
            {
                return this._DescFields2;
            }
            set
            {
                this._DescFields2 = value;
            }
        }

        private List<string> _AssocFields2;
        /// <summary></summary>
        public List<string> AssocFields2
        {
            get
            {
                return this._AssocFields2;
            }
            set
            {
                this._AssocFields2 = value;
            }
        }

        private string _PKField2;
        /// <summary></summary>
        public string PKField2
        {
            get
            {
                return this._PKField2;
            }
            set
            {
                this._PKField2 = value;
            }
        }
        #endregion

        #region comparers and weights 
        private Dictionary<string,string> _Field1_Field2_Mappings;
        /// <summary></summary>
        public Dictionary<string,string> Field1_Field2_Mappings
        {
            get
            {
                return this._Field1_Field2_Mappings;
            }
            set
            {
                this._Field1_Field2_Mappings = value;
            }
        }

        private Dictionary<string,double> _FieldWeights;
        /// <summary>field name 1 -> weight</summary>
        public Dictionary<string,double> FieldWeights
        {
            get
            {
                return this._FieldWeights;
            }
            set
            {
                this._FieldWeights = value;
            }
        }

        private Dictionary<string,double> _FieldPenalties;
        /// <summary></summary>
        public Dictionary<string,double> FieldPenalties
        {
            get
            {
                return this._FieldPenalties;
            }
            set
            {
                this._FieldPenalties = value;
            }
        }

        private Dictionary<string, string> _FieldComparers;
        /// <summary></summary>
        public Dictionary<string, string> FieldComparers
        {
            get
            {
                return this._FieldComparers;
            }
            set
            {
                this._FieldComparers = value;
            }
        }

        private double _AssocThreshold;
        /// <summary></summary>
        public double AssocThreshold
        {
            get
            {
                return this._AssocThreshold;
            }
            set
            {
                this._AssocThreshold = value;
            }
        }

        private double _LinkThreshold;
        /// <summary></summary>
        public double LinkThreshold
        {
            get
            {
                return this._LinkThreshold;
            }
            set
            {
                this._LinkThreshold = value;
            }
        }
        #endregion

        #region output 
        private string _OutputTableName;
        /// <summary></summary>
        public string OutputTableName
        {
            get
            {
                return this._OutputTableName;
            }
            set
            {
                this._OutputTableName = value;
            }
        }

        private string _EntityIDField1;
        /// <summary></summary>
        public string EntityIDField1
        {
            get
            {
                return this._EntityIDField1;
            }
            set
            {
                this._EntityIDField1 = value;
            }
        }

        private string _EntityIDField2;
        /// <summary></summary>
        public string EntityIDField2
        {
            get
            {
                return this._EntityIDField2;
            }
            set
            {
                this._EntityIDField2 = value;
            }
        }
        #endregion

        #region ctor
        /// <summary>
        /// 
        /// </summary>
        public EntityLinkInstruction():base(Guid.Empty,Guid.Empty)
        {
            this._ConnStr1 = string.Empty;
            this._SchemaName1 = string.Empty;
            this._TableName1 = string.Empty;
            this._Sql1 = string.Empty;
            this._FieldTransformations1=new Dictionary<string, string>();
            this._DescFields1=new List<string>();
            this._AssocFields1=new List<string>();
            this._PKField1 = string.Empty;

            this._ConnStr2 = string.Empty;
            this._SchemaName2 = string.Empty;
            this._TableName2 = string.Empty;
            this._Sql2 = string.Empty;
            this._FieldTransformations2 = new Dictionary<string, string>();
            this._DescFields2 = new List<string>();
            this._AssocFields2 = new List<string>();
            this._PKField2 = string.Empty;

            this._Field1_Field2_Mappings=new Dictionary<string, string>();
            this._FieldWeights=new Dictionary<string, double>();
            this._FieldPenalties=new Dictionary<string, double>();
            this._FieldComparers = new Dictionary<string, string>();
            this._AssocThreshold = 0.30;
            this._LinkThreshold = 0.55;

            this._OutputTableName = string.Empty;
            this._EntityIDField1 = this._PKField1;
            this._EntityIDField2 = this._PKField2;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="componentID"></param>
        /// <param name="jobTicketID"></param>
        /// <param name="connStr1"></param>
        /// <param name="schema1"></param>
        /// <param name="tbl1"></param>
        /// <param name="sql1"></param>
        /// <param name="transformations1"></param>
        /// <param name="descFields1"></param>
        /// <param name="assocFields1"></param>
        /// <param name="pkField1"></param>
        /// <param name="connStr2"></param>
        /// <param name="schema2"></param>
        /// <param name="tbl2"></param>
        /// <param name="sql2"></param>
        /// <param name="transformations2"></param>
        /// <param name="descFields2"></param>
        /// <param name="assocFields2"></param>
        /// <param name="pkField2"></param>
        /// <param name="mappings"></param>
        /// <param name="weights"></param>
        /// <param name="penalties"></param>
        /// <param name="comparers"></param>
        /// <param name="assocThreshold"></param>
        /// <param name="linkThreshold"></param>
        public EntityLinkInstruction(Guid componentID, Guid jobTicketID,
            string connStr1,string schema1, string tbl1,string sql1,
            Dictionary<string,string> transformations1, 
            List<string> descFields1, List<string> assocFields1, string pkField1,
            string connStr2, string schema2, string tbl2, string sql2,
            Dictionary<string, string> transformations2,
            List<string> descFields2, List<string> assocFields2, string pkField2,
            Dictionary<string,string> mappings,
            Dictionary<string,double> weights,
            Dictionary<string,double> penalties,
            Dictionary<string, string> comparers,
            double assocThreshold, double linkThreshold,
            string outputTbl, string entityIDField1, string entityIDField2)
            : base(componentID, jobTicketID)
        {
            this._ConnStr1 = connStr1;
            this._SchemaName1 = schema1;
            this._TableName1 = tbl1;
            this._Sql1 = sql1;
            this._FieldTransformations1 = transformations1;
            this._DescFields1 = descFields1;
            this._AssocFields1 = assocFields1;
            this._PKField1 = pkField1;

            this._ConnStr2 = connStr2;
            this._SchemaName2 = schema2;
            this._TableName2 = tbl2;
            this._Sql2 = sql2;
            this._FieldTransformations2 = transformations2;
            this._DescFields2 = descFields2;
            this._AssocFields2 = assocFields2;
            this._PKField2 = pkField2;

            this._Field1_Field2_Mappings = mappings;
            this._FieldWeights = weights;
            this._FieldPenalties = penalties;
            this._FieldComparers = comparers;
            this._AssocThreshold = assocThreshold;
            this._LinkThreshold = linkThreshold;

            this._OutputTableName = outputTbl;
            this._EntityIDField1 = entityIDField1;
            this._EntityIDField2 = entityIDField2;
        }
        #endregion

        #region Overrides of InstructionBase

        /// <summary>
        /// serialize settings to xml
        /// the function below is mainly for storing into database as serialized form instead of string
        /// </summary>
        public override XmlNode Serialize(ref XmlDocument xDoc, ref XmlNode parentNode)
        {
            XmlNode xNode = XmlDataUtil.AddElement(ref xDoc, parentNode, "Instruction");
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Type", this.GetType().FullName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ComponentID", this.OwnerComponentID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "WorkerID", this.OwnerWorkerInstanceID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SubmitTime", this.SubmitTime.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "StartTime", this.StartTime.ToString());

            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ConnStr1", this.ConnStr1);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Schema1", this.SchemaName1);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Table1", this.TableName1);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Sql1", this.Sql1);
            XmlNode transformNodes1 = XmlDataUtil.AddElement(ref xDoc, xNode, "FieldTransformations1");
            if(this.FieldTransformations1 !=null && this.FieldTransformations1.Count>0)
            {
                foreach(string fieldName in this.FieldTransformations1.Keys)
                {
                    XmlNode transformNode1 = XmlDataUtil.AddElement(ref xDoc, transformNodes1, "FieldTransformation1");
                    XmlDataUtil.UpdateAttribute(ref xDoc, transformNode1,"FieldName",fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, transformNode1,"Code",this.FieldTransformations1[fieldName]);
                }
            }
            XmlNode descNodes1 = XmlDataUtil.AddElement(ref xDoc, xNode, "DescFields1");
            if(this.DescFields1!=null && this.DescFields1.Count>0)
            {
                foreach(string descField in this.DescFields1)
                {
                    XmlNode descNode = XmlDataUtil.AddElement(ref xDoc, descNodes1, "DescField1");
                    XmlDataUtil.UpdateAttribute(ref xDoc, descNode, "FieldName", descField);
                }
            }
            XmlNode assocNodes1 = XmlDataUtil.AddElement(ref xDoc, xNode, "AssocFields1");
            if(this.AssocFields1!=null && this.AssocFields1.Count>0)
            {
                foreach(string assocField in this.AssocFields1)
                {
                    XmlNode assocNode = XmlDataUtil.AddElement(ref xDoc, assocNodes1, "AssocField1");
                    XmlDataUtil.UpdateAttribute(ref xDoc, assocNode,"FieldName",assocField);
                }
            }
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode,"PKField1",this.PKField1);

            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ConnStr2", this.ConnStr2);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Schema2", this.SchemaName2);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Table2", this.TableName2);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Sql2", this.Sql2);
            XmlNode transformNodes2 = XmlDataUtil.AddElement(ref xDoc, xNode, "FieldTransformations2");
            if (this.FieldTransformations2 != null && this.FieldTransformations2.Count > 0)
            {
                foreach (string fieldName in this.FieldTransformations2.Keys)
                {
                    XmlNode transformNode2 = XmlDataUtil.AddElement(ref xDoc, transformNodes2, "FieldTransformation2");
                    XmlDataUtil.UpdateAttribute(ref xDoc, transformNode2, "FieldName", fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, transformNode2, "Code", this.FieldTransformations2[fieldName]);
                }
            }
            XmlNode descNodes2 = XmlDataUtil.AddElement(ref xDoc, xNode, "DescFields2");
            if (this.DescFields2 != null && this.DescFields2.Count > 0)
            {
                foreach (string descField in this.DescFields2)
                {
                    XmlNode descNode = XmlDataUtil.AddElement(ref xDoc, descNodes2, "DescField2");
                    XmlDataUtil.UpdateAttribute(ref xDoc, descNode, "FieldName", descField);
                }
            }
            XmlNode assocNodes2 = XmlDataUtil.AddElement(ref xDoc, xNode, "AssocFields2");
            if (this.AssocFields2 != null && this.AssocFields2.Count > 0)
            {
                foreach (string assocField in this.AssocFields2)
                {
                    XmlNode assocNode = XmlDataUtil.AddElement(ref xDoc, assocNodes2, "AssocField2");
                    XmlDataUtil.UpdateAttribute(ref xDoc, assocNode, "FieldName", assocField);
                }
            }
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "PKField2", this.PKField2);

            XmlNode mappingNodes = XmlDataUtil.AddElement(ref xDoc, xNode, "Mappings");
            if (this.Field1_Field2_Mappings != null && this.Field1_Field2_Mappings.Count > 0)
            {
                foreach (string field1 in this.Field1_Field2_Mappings.Keys)
                {
                    XmlNode mappingNode = XmlDataUtil.AddElement(ref xDoc, mappingNodes, "Mapping");
                    XmlDataUtil.UpdateAttribute(ref xDoc, mappingNode,"Field1",field1);
                    XmlDataUtil.UpdateAttribute(ref xDoc, mappingNode, "Field2",this.Field1_Field2_Mappings[field1]);
                }
            }
            XmlNode weightNodes = XmlDataUtil.AddElement(ref xDoc, xNode, "Weights");
            if (this.FieldWeights != null && this.FieldWeights.Count > 0)
            {
                foreach (string fieldName in this.FieldWeights.Keys)
                {
                    XmlNode weigntNode = XmlDataUtil.AddElement(ref xDoc, weightNodes, "Weight");
                    XmlDataUtil.UpdateAttribute(ref xDoc, weigntNode, "FieldName", fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, weigntNode, "Weight", this.FieldWeights[fieldName].ToString());
                }
            }
            XmlNode penaltyNodes = XmlDataUtil.AddElement(ref xDoc, xNode, "Penalties");
            if(this.FieldPenalties !=null && this.FieldPenalties.Count>0)
            {
                foreach(string fieldName in this.FieldPenalties.Keys)
                {
                    XmlNode penaltyNode = XmlDataUtil.AddElement(ref xDoc, penaltyNodes, "Penalty");
                    XmlDataUtil.UpdateAttribute(ref xDoc, penaltyNode,"FieldName",fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, penaltyNode, "Penalty",this.FieldPenalties[fieldName].ToString());
                }
            }
            XmlNode comparerNodes = XmlDataUtil.AddElement(ref xDoc, xNode, "Comparers");
            if(this.FieldComparers !=null && this.FieldComparers.Count>0)
            {
                foreach(string fieldName in this.FieldComparers.Keys)
                {
                    XmlNode comparerNode = XmlDataUtil.AddElement(ref xDoc, comparerNodes, "Comparer");
                    XmlDataUtil.UpdateAttribute(ref xDoc, comparerNode,"FieldName", fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, comparerNode, "Comparer",
                                           this.FieldComparers[fieldName]);
                }
            }
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "AssocThreshold",this.AssocThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "LinkThreshold",this.LinkThreshold.ToString());

            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "OutputTableName", this.OutputTableName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "EntityIDField1", this.EntityIDField1);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "EntityIDField2", this.EntityIDField2);

            return xNode;
        }

        /// <summary>
        /// instantiated from xml
        /// "&lt;Instructions&gt;&lt;Instruction&gt;&lt;/Instruction&gt;&lt;/Instruction&gt;", must
        /// instantiate from first child of root node
        /// </summary>
        public override InstructionBase Instantiate(XmlNode xNode)
        {
            if (xNode.Attributes.GetNamedItem("Type").Value == this.GetType().FullName)
            {
                EntityLinkInstruction instruction = new EntityLinkInstruction();
                instruction.OwnerComponentID = new Guid(xNode.Attributes.GetNamedItem("ComponentID").Value);
                instruction.OwnerWorkerInstanceID = new Guid(xNode.Attributes.GetNamedItem("WorkerID").Value);
                instruction.SubmitTime = DateTime.Parse(xNode.Attributes.GetNamedItem("SubmitTime").Value);
                instruction.StartTime = DateTime.Parse(xNode.Attributes.GetNamedItem("StartTime").Value);

                instruction.ConnStr1 = XmlDataUtil.GetAttributeValue(xNode, "ConnStr1", "");
                instruction.SchemaName1 = XmlDataUtil.GetAttributeValue(xNode, "Schema1", "");
                instruction.TableName1 = XmlDataUtil.GetAttributeValue(xNode, "Table1", "");
                instruction.Sql1 = XmlDataUtil.GetAttributeValue(xNode, "Sql1", "");
                instruction.FieldTransformations1=new Dictionary<string, string>();
                XmlNodeList transformNodes1 = xNode.SelectNodes("FieldTransformations1/FieldTransformation1");
                if(transformNodes1 !=null && transformNodes1.Count>0)
                {
                    foreach(XmlNode transformNode1 in transformNodes1)
                    {
                        string fieldName = XmlDataUtil.GetAttributeValue(transformNode1, "FieldName", "");
                        string code = XmlDataUtil.GetAttributeValue(transformNode1, "Code", "");
                        instruction.FieldTransformations1.Add(fieldName,code);
                    }
                }
                instruction.DescFields1=new List<string>();
                XmlNodeList descNodes1 = xNode.SelectNodes("DescFields1/DescField1");
                if(descNodes1 !=null && descNodes1.Count>0)
                {
                    foreach(XmlNode descNode1 in descNodes1)
                    {
                        instruction.DescFields1.Add(XmlDataUtil.GetAttributeValue(descNode1, "FieldName", ""));   
                    }
                }
                instruction.AssocFields1=new List<string>();
                XmlNodeList assocNodes1 = xNode.SelectNodes("AssocFields1/AssocField1");
                if(assocNodes1 !=null && assocNodes1.Count>0)
                {
                    foreach(XmlNode assocNode1 in assocNodes1)
                    {
                        instruction.AssocFields1.Add(
                            XmlDataUtil.GetAttributeValue(assocNode1, "FieldName", ""));
                    }
                }
                instruction.PKField1 = XmlDataUtil.GetAttributeValue(xNode, "PKField1", "");

                instruction.ConnStr2 = XmlDataUtil.GetAttributeValue(xNode, "ConnStr2", "");
                instruction.SchemaName2 = XmlDataUtil.GetAttributeValue(xNode, "Schema2", "");
                instruction.TableName2 = XmlDataUtil.GetAttributeValue(xNode, "Table2", "");
                instruction.Sql2 = XmlDataUtil.GetAttributeValue(xNode, "Sql2", "");
                instruction.FieldTransformations2 = new Dictionary<string, string>();
                XmlNodeList transformNodes2 = xNode.SelectNodes("FieldTransformations2/FieldTransformation2");
                if (transformNodes2 != null && transformNodes2.Count > 0)
                {
                    foreach (XmlNode transformNode2 in transformNodes2)
                    {
                        string fieldName = XmlDataUtil.GetAttributeValue(transformNode2, "FieldName", "");
                        string code = XmlDataUtil.GetAttributeValue(transformNode2, "Code", "");
                        instruction.FieldTransformations2.Add(fieldName, code);
                    }
                }
                instruction.DescFields2 = new List<string>();
                XmlNodeList descNodes2 = xNode.SelectNodes("DescFields2/DescField2");
                if (descNodes2 != null && descNodes2.Count > 0)
                {
                    foreach (XmlNode descNode2 in descNodes2)
                    {
                        instruction.DescFields2.Add(XmlDataUtil.GetAttributeValue(descNode2, "FieldName", ""));
                    }
                }
                instruction.AssocFields2 = new List<string>();
                XmlNodeList assocNodes2 = xNode.SelectNodes("AssocFields2/AssocField2");
                if (assocNodes2 != null && assocNodes2.Count > 0)
                {
                    foreach (XmlNode assocNode2 in assocNodes2)
                    {
                        instruction.AssocFields2.Add(
                            XmlDataUtil.GetAttributeValue(assocNode2, "FieldName", ""));
                    }
                }
                instruction.PKField2 = XmlDataUtil.GetAttributeValue(xNode, "PKField2", "");

                instruction.Field1_Field2_Mappings=new Dictionary<string, string>();
                XmlNodeList mappingNodes = xNode.SelectNodes("Mappings/Mapping");
                if(mappingNodes !=null && mappingNodes.Count>0)
                {
                    foreach(XmlNode mappingNode in mappingNodes)
                    {
                        string field1 = XmlDataUtil.GetAttributeValue(mappingNode, "Field1", "");
                        string field2 = XmlDataUtil.GetAttributeValue(mappingNode, "Field2", "");
                        instruction.Field1_Field2_Mappings.Add(field1,field2);
                    }
                }
                instruction.FieldWeights=new Dictionary<string, double>();
                XmlNodeList weigntNodes = xNode.SelectNodes("Weights/Weight");
                if(weigntNodes !=null && weigntNodes.Count>0)
                {
                    foreach(XmlNode weigntNode in weigntNodes)
                    {
                        string fieldName = XmlDataUtil.GetAttributeValue(weigntNode, "FieldName", "");
                        double weight = double.Parse(XmlDataUtil.GetAttributeValue(weigntNode, "Weight", "0"));
                        instruction.FieldWeights.Add(fieldName,weight);
                    }
                }
                instruction.FieldPenalties=new Dictionary<string, double>();
                XmlNodeList penaltyNodes = xNode.SelectNodes("Penalties/Penalty");
                if(penaltyNodes !=null && penaltyNodes.Count>0)
                {
                    foreach(XmlNode penaltyNode in penaltyNodes)
                    {
                        string fieldName = XmlDataUtil.GetAttributeValue(penaltyNode, "FieldName", "");
                        double penalty = double.Parse(XmlDataUtil.GetAttributeValue(penaltyNode, "Penalty", "0"));
                        instruction.FieldPenalties.Add(fieldName,penalty);
                    }
                }
                instruction.FieldComparers = new Dictionary<string, string>();
                XmlNodeList comparerNodes = xNode.SelectNodes("Comparers/Comparer");
                if(comparerNodes !=null && comparerNodes.Count>0)
                {
                    foreach(XmlNode comparerNode in comparerNodes)
                    {
                        string fieldName = XmlDataUtil.GetAttributeValue(comparerNode, "FieldName", "");
                        string comparerTypeName = XmlDataUtil.GetAttributeValue(comparerNode, "Comparer", "");
                        instruction.FieldComparers.Add(fieldName, comparerTypeName);
                    }
                }
                instruction.AssocThreshold = double.Parse(XmlDataUtil.GetAttributeValue(xNode, "AssocThreshold", "0"));
                instruction.LinkThreshold = double.Parse(XmlDataUtil.GetAttributeValue(xNode, "LinkThreshold", "0"));

                instruction.OutputTableName = XmlDataUtil.GetAttributeValue(xNode, "OutputTableName", "");
                instruction.EntityIDField1 = XmlDataUtil.GetAttributeValue(xNode, "EntityIDField1", "");
                instruction.EntityIDField2 = XmlDataUtil.GetAttributeValue(xNode, "EntityIDField2", "");

                return instruction;
            }
            else
            {
                throw new Exception("Unable to instantiate instruction: invalid type specified in xml node");
            }
        }

        #endregion
    }
}
